基本型別
- primitive type 原始型態
- null
- undefined
- string
- number
- boolean
- symbol(ES6)
- Immutable
- by value
物件型別
- 除了以上基本型別之外都是 object type
- array
- function
- data
- mutable
by reference
例外
若是使用 object literal 的方式來建立物件,則會變成 by Value,新增了一個記憶體的位置。
by value、by reference、by sharing
JavaScript 是「傳值」或「傳址」?
談談 JavaScript 中 by reference 和 by value 的重要觀念
如何知道型態
typeof
console.log(typeof true)
但有些無法靠 typeof 得知
console.log(typepf null)
-> object
console.log(typepf function)
-> function想判斷是不是陣列
console.log(Array.isArray([]))
較舊的瀏覽器沒有這個方法的話可以用
console.log(Object.prototype.toString,call(null))
[object Array]
賦值
primitive type
object type
順帶一提
- 賦值
=
- 判別物件
==
- 多了有無型態轉換
===
(指向同一個記憶體位置時才成立)
NaN
Not a number
- 也是數字的一種,但不是真的數字
- 不等於任何東西,甚至不等於自己
isNaN 可以查是不是,但較舊瀏覽器也是不支援
JS Comparison Table
isNaN()
ES6
宣告方式
- let 宣告變數(與 var 差不多,但作用域僅限 block)
- const 宣告常數(宣告完就不能改變)
作用域 scope
變數的生存範圍
- global variable 全域變數
scope chain
inner scope -> test scope -> global scope
Hoisting
Hoisting 順序
function -> 參數 -> variable
直接給例
console.log(age)
var age = 100
輸出結果:undefined
咦?照理來說,程式碼由上往下執行,不是應該 age is not defined
嗎?很奇怪吧,這就是 JS 中的 Hoisting,變數的宣告會被提升到最上面。
再來,以我目前的理解及實作,Hoisting 最重要的地方在於讓程式碼中 function 可以順利宣告及呼叫。至於變數宣告,倒是可以透過良好 coding 習慣解決的。
Execution context 執行環境
- 單執行緒
- 同步執行
- 只會有一個 Global Execution
- function Execution 沒有限制
- 只要呼叫函式就會建立執行環境(call function -> Execution context),即使是自己呼叫自己
Execution context (current) |
---|
Execution context (N+2) |
Execution context (N+1) |
Execution context (N) |
Global Execution context |
function 的資訊都在 Execution context 裡,而每個 Execution context 裡都會有相對應的 variable object(暫稱 VO)。
首先,你可以把 VO 想像成就是一個 JavaScript 的物件就好。
再來,VO 什麼時候會用到?你在存取值的時候會用到,例如說 var a = 10 這一句,之前有講過可以分成左右兩塊:
- var a:去 VO 裡面新增一個屬性叫做 a(如果沒有 a 這個屬性的話)並初始化成 undefined
- a = 10:先在 VO 裡面找到叫做 a 的屬性,找到之後設定為 10
更多關於 VO.... 請左轉至 我知道你懂 hoisting,可是你了解到多深? 深入了解,大概滑鼠往下滑 10 下就到了
Execution context more
每 call function 一次,會建立一個新的 EC,但每次 JS 內部調用一個 EC 都會有兩個階段
- 建立階段 —— 當函式被呼叫但執行內部程式碼前
- 建立一個作用域鍊(scope chain)
- 建立變數、function、參數
- 設定 this 的值
- 執行階段
- 賦值、設定 function 的參考和解譯執行程式碼
所以如果我們把 EC 想像成一個物件的話,大概會像這樣
executionContextObject = {
scopeChain: { /* 變數物件 + 所有父代執行環境物件的變數物件*/},
variableObject: {/* 函式的參數/引數,內部的變數和函式*/ },
this: {}
}
Scope 作用域
變數活性區
- global
- block(ES6)
- function
clouser 閉包
物件導向
myWallet.add(1)
以前可能是用 function call
- class -> this
ES6 之前JS還沒有物件導向方法
setter
getterES5 時期,透過 .prototype.getName()
prototype 原型鍊
prototype
__proto__
constructor
Object.prototype
Function.prototype
new
物件導向的繼承 Inheritance
constrecture
super
this (記得先盡量搞懂物件導向)
- this 是 JavaScript 的一個關鍵字。
- this 是 function 執行時,自動生成的一個內部物件。
- 隨著 function 執行場合的不同,this 所指向的值,也會有所不同。
- 在大多數的情況下, this 代表的就是呼叫 function 的物件 (Owner Object of the function)。
—— What’s THIS in JavaScript ?
- 嚴格模式底下就都是undefined
- 非嚴格模式,瀏覽器底下是window
- 非嚴格模式,node.js 底下是global
—— 淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂
' use strict ' ;
.call()
.apply()
function log() {
console.log(this);
}
var a = { a: 1, log: log };
var b = { a: 2, log: log };
log(); // undefined
a.log(); // a
b.log.apply(a) // a
怎麼理解繼承?
文章先嗑
Javascript继承机制的设计思想
Javascript 面向对象编程(一):封装
Javascript面向对象编程(二):构造函数的继承
Javascript面向对象编程(二):构造函数的继承
嗑完文章
一開始作者設計的時候其實認為不需要設計繼承機制,但 JS 裡面所有數據類型都是對象(object),所以最後還是引入了物件導向的 new 命令,但在 Java 或 C++ 裡使用 new 命令時都會調用「類(Class)」的構造函數,但可能礙於是要做出簡易的腳本語言不增加初學者負擔,於是簡化了設計,所以在 JS 裡的 new 命令後面跟得不是 Class 而是構造函數(constructor)。
但這樣會有一個缺點,就是無法共享屬性和方法造成滿大的資源浪費,因此作者決定為構造函數(constructor)設一個 prototype 屬性。
constructor
为了解决从原型对象生成实例的问题,Javascript提供了一个构造函数(Constructor)模式。
所谓"构造函数",其实就是一个普通函数,但是内部使用了this变量。对构造函数使用new运算符,就能生成实例,并且this变量会绑定在实例对象上。
prototype
prototype 這個屬性包含了一個對象,所有想共享的屬性、方法都放在這個對象裡; 其他不需要的就放在剛提到的構造函數裡(constructor)。